//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
// (c) Andrew V. Zaharov (andy_zah@mail.ru), 2006
//
// Register map in MapSource.
// NOTE: the code is incomplete!
//

# include "StdAfx.h"
# include "GarminImgRegister.h"
# include "GarminImg.h"
# include "GarminTdb.h"
# include "FileWriter.h"
# include "ParseHelpers.h"
# include "Wrappers.h"
# include "Reg.h"
# include "Globals.h"

static
void _RegisterIMG (
	const char * _strImgFile,
	const char * _strCopy1, const char * _strCopy2,
	const char * _strMapSet, const char * _strCD
) {

	// TODO: make full path for _strImgFile

	CRegistryKey hkeyMSProducts (HKEY_LOCAL_MACHINE, "SOFTWARE\\Garmin\\MapSource\\Products");
	if (! hkeyMSProducts.IsValid ()) {
		ErrorMsgBox ("Can't open registry of MapSource products.");
		return;
	}
/*
	size_t cProductIdx = 0;
	while (true) {
		char strProductCode [20];
		const LONG lr = ::RegEnumKey (hkeyMSProducts, cProductIdx ++, strProductCode, sizeof (strProductCode));
		if (lr == ERROR_NO_MORE_ITEMS)
			break;
		if (lr != ERROR_SUCCESS)
			continue;

		strProductCode;
	}
*/

	//
	// Read data from IMG.
	//

	const CFileForRead hImgFile (_strImgFile);
	if (! hImgFile.IsValid ()) {
		ErrorMsgBox ("Can't open file:\n %s", _strImgFile);
		return;
	}

	const size_t cImgFileSize = ::GetFileSize (hImgFile, NULL);
	BYTE * const pImgData = new BYTE [cImgFileSize];
	CByteArrayHolder bah (pImgData);

	DWORD dwRead = 0;
	::ReadFile (hImgFile, pImgData, cImgFileSize, & dwRead, NULL);
	if (dwRead < cImgFileSize) {
		ErrorMsgBox ("Can't read file '%s'.", _strImgFile);
		return;
	}

	// Decode the IMG file.
	const BYTE btXORMask = pImgData [0x10] ^ BYTE ('D');
	for (size_t c = 0; c < cImgFileSize; ++ c)
		pImgData [c] ^= btXORMask;

	const CImgHeader * const pImgHeader = reinterpret_cast<const CImgHeader *> (pImgData);
	if (::memcmp (pImgHeader->strSignature, c_strImgSignature, sizeof (pImgHeader->strSignature)) != 0) {
		ErrorMsgBox ("This is not a valid MapSource IMG file.");
		return;
	}
	const size_t cBlockSize = pImgHeader->GetBlockSize ();

	string_t strName (
		pImgHeader->strName,
		GetLineLen (
			pImgHeader->strName,
			pImgHeader->strName + sizeof (pImgHeader->strName)
		)
	);
	strName.append (
		pImgHeader->strName2,
		GetLineLen (
			pImgHeader->strName2,
			pImgHeader->strName2 + sizeof (pImgHeader->strName2) - 1
		)
	);

	size_t cSections = 0;
	size_t cRgnOffset = 0, cRgnLength = 0;
	size_t cTreOffset = 0, cTreLength = 0;
	size_t cLblOffset = 0, cLblLength = 0;
	size_t cNetOffset = 0, cNetLength = 0;
	size_t cNodOffset = 0, cNodLength = 0;

	char strSubFileName [8];
	strSubFileName [0] = 0;

	DWORD dwImgId = 0;

	const size_t cIdxBlocks = (pImgHeader->dwDataOffset - (sizeof (CImgHeader) - sizeof (pImgHeader->FAT)))/sizeof (CFileIndexBlock);
	for (size_t cIdxBlock = 0; cIdxBlock < cIdxBlocks; ++ cIdxBlock) {
		const CFileIndexBlock & fib = pImgHeader->FAT [cIdxBlock];
		if (fib.btValid != 1 || fib.btIndex > 0)
			continue;

		// Validate sub-file name.
		if (strSubFileName [0] == 0) {
			assert (sizeof (strSubFileName) == sizeof (fib.strName));
			::memcpy (strSubFileName, fib.strName, sizeof (strSubFileName));

			if (strSubFileName [0] == 'I')
				dwImgId = ParseHex (strSubFileName + 1);
			else
				dwImgId = ParseDecU (strSubFileName);
		} else {
			if (::memcmp (strSubFileName, fib.strName, sizeof (strSubFileName)) != 0)
				continue;
		}

		if (::memcmp        (fib.strType, "RGN", sizeof (fib.strType)) == 0) {
			cRgnOffset = fib.wStartBlock*cBlockSize;
			cRgnLength = fib.dwLength;
			++ cSections;
		} else if (::memcmp (fib.strType, "TRE", sizeof (fib.strType)) == 0) {
			cTreOffset = fib.wStartBlock*cBlockSize;
			cTreLength = fib.dwLength;
			++ cSections;
		} else if (::memcmp (fib.strType, "LBL", sizeof (fib.strType)) == 0) {
			cLblOffset = fib.wStartBlock*cBlockSize;
			cLblLength = fib.dwLength;
			++ cSections;
		} else {
			if (::memcmp    (fib.strType, "NET", sizeof (fib.strType)) == 0) {
				cNetOffset = fib.wStartBlock*cBlockSize;
				cNetLength = fib.dwLength;
				++ cSections;
			} else if (::memcmp (fib.strType, "NOD", sizeof (fib.strType)) == 0) {
				cNodOffset = fib.wStartBlock*cBlockSize;
				cNodLength = fib.dwLength;
				++ cSections;
			}
		}
	}

	cSections = cSections >= 3 ? cSections : 3;

	const CTreHeader * const pTreHeader = reinterpret_cast<const CTreHeader *> (pImgData + cTreOffset);

	/////////////////////////////////////////

	// Prepare name of TDB file.
	char strTdbFile [MAX_PATH];
	::strcpy (strTdbFile, _strImgFile);
	::PathRenameExtension (strTdbFile, ".tdb");

	if (::GetFileAttributes (strTdbFile) != -1) {
		if (YesNoMsgBox ("The TDB file already exists. Overwrite it?") == false)
			return;
	}

	//
	// Open the TDB file.
	//
	const CFileForWrite hTdbFile (strTdbFile);
	if (! hTdbFile.IsValid ()) {
		ErrorMsgBox ("Can't create file:\n %s", strTdbFile);
		return;
	}
	CFileWriter fw (hTdbFile);

	const WORD ver2 = 100;

	// Save the product information.
	CTdbProduct product;
	product.btType = 'P';
	product.dwProductId = dwImgId & 0xffff;
	product.dwVer = 300;
	product.wLen = sizeof (product) - sizeof (CTdbRecordHdr) - sizeof (product.strName) +
		strName.size () + 1 + sizeof (WORD) + ::strlen (_strCD) + 1;
	fw.WriteBin (& product, sizeof (product) - sizeof (product.strName) );
	fw.WriteS (strName);
	fw.WriteBYTE (0);
	CTdbProduct2 product2;
	product2.wDataVer = ver2;
	fw.WriteBin (& product2, sizeof (product2) - sizeof (product2.strCDSetName) );
	fw.WriteS (_strCD);
	fw.WriteBYTE (0);

	// Save the copyright information.
	CTdbCopyrights copyrs;
	copyrs.btType = 'D';
	copyrs.wLen = ::strlen (_strCopy1) + 1 + ::strlen (_strCopy2) + 1 +
		1 + 1 + 2 + 2 + 1 + 1;
	fw.WriteBin (& copyrs, sizeof (copyrs) - sizeof (copyrs.c0));
	fw.WriteBYTE (6);
	fw.WriteWORD (1);
	fw.WriteBYTE (0);
	fw.WriteS (_strCopy1);
	fw.WriteBYTE (0);
	fw.WriteBYTE (6);
	fw.WriteWORD (2);
	fw.WriteBYTE (0);
	fw.WriteS (_strCopy2);
	fw.WriteBYTE (0);

	// Save the overview map reference.
	CTdbImgRef img;
	img.btType = 'B';
	img.wLen = sizeof (img) - sizeof (img.strDisplayName) - sizeof (CTdbRecordHdr) +
		::strlen (_strMapSet) + 1;
	img.dwImgId = 8;
	img.dwLandId = 0;
	img.lNorth = ConvertB3ToDWORD (pTreHeader->b3MapNorth) << 8;
	img.lEast  = ConvertB3ToDWORD (pTreHeader->b3MapEast)  << 8;
	img.lSouth = ConvertB3ToDWORD (pTreHeader->b3MapSouth) << 8;
	img.lWest  = ConvertB3ToDWORD (pTreHeader->b3MapWest)  << 8;
	fw.WriteBin (& img, sizeof (img) - sizeof (img.strDisplayName));
	fw.WriteS (_strMapSet);
	fw.WriteBYTE (0);

	// Save the detail map reference.
	img.btType = 'L';
	img.wLen = sizeof (img) - sizeof (img.strDisplayName) - sizeof (CTdbRecordHdr) +
		strName.size () + 1 + 2*sizeof (WORD) + cSections*sizeof (DWORD) + sizeof (BYTE) +
		(sizeof (strSubFileName) + 5) * ((cSections >= 4)? (cSections >= 5)? 5:4 : 3) +
		sizeof (WORD)*3 + sizeof (WORD);
	img.dwImgId = dwImgId;
	img.dwLandId = 8;
	fw.WriteBin (& img, sizeof (img) - sizeof (img.strDisplayName));
	fw.WriteS (strName);
	fw.WriteBYTE (0);
	fw.WriteWORD (cSections + 1); // ????
	fw.WriteWORD (cSections);
	fw.WriteDWORD (cTreLength);
	fw.WriteDWORD (cRgnLength);
	fw.WriteDWORD (cLblLength);
	if (cSections >= 4) {
		fw.WriteDWORD (cNetLength);
		if (cSections >= 5)
			fw.WriteDWORD (cNodLength);
	}
	fw.WriteBYTE (1);
	fw.WriteWORD (1);
	fw.WriteWORD (255);
	fw.WriteWORD (0);
	fw.WriteBin ( & strSubFileName, sizeof (strSubFileName)); fw.WriteS (".RGN"); fw.WriteBYTE (0);
	fw.WriteBin ( & strSubFileName, sizeof (strSubFileName)); fw.WriteS (".TRE"); fw.WriteBYTE (0);
	fw.WriteBin ( & strSubFileName, sizeof (strSubFileName)); fw.WriteS (".LBL"); fw.WriteBYTE (0);
	if (cSections >= 4) {
		fw.WriteBin ( & strSubFileName, sizeof (strSubFileName));	fw.WriteS (".NET");	fw.WriteBYTE (0);
		if (cSections >= 5) { fw.WriteBin ( & strSubFileName, sizeof (strSubFileName));	fw.WriteS (".NOD");	fw.WriteBYTE (0); }
	}
	fw.WriteWORD (0);

	fw.FlushBuffer ();

	//
	// Write to MapSource registry.
	//

	// Prepare product key: just file path with slashes reverted
	// (to avoid their misinterpretation as subkey separators).
	char strProduct [MAX_PATH + 1];
	::strncpy (strProduct, strName.c_str (), MAX_PATH);
	for (char * p = strProduct; * p; ++ p)
		if (* p == '\\')
			* p = '/';

	// Prepare path to detail IMGs.
	char strImgPath [MAX_PATH + 1];
	::strncpy (strImgPath, _strImgFile, MAX_PATH);
	::PathRemoveFileSpec (strImgPath);

	CRegistryKey hkeyMSProduct (hkeyMSProducts, strProduct);
	if (hkeyMSProduct.IsValid ()) {
		// TODO: ask user to overwrite
	} else
		hkeyMSProduct.Create (hkeyMSProducts, strProduct);
	hkeyMSProduct.SetValue ("Bmap", _strImgFile);
	hkeyMSProduct.SetValue ("Loc", strImgPath);
	if (hkeyMSProduct.SetValue ("Tdb", strTdbFile) != ERROR_SUCCESS)
		ErrorMsgBox ("Failed to register the map in the MapSource registry of products.");
}

void RegisterIMG (const string_t & _strPath) {
# ifdef _DEBUG
	_RegisterIMG (_strPath.c_str (), "Copy1", "Copy2", "Map Set", "CD");
# endif // _DEBUG
}